package com.tms.warehouse.mappings;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.tms.crud.Attribute;
import com.tms.crud.PropertyScanner;
import com.tms.warehouse.entity.Identificable;

public abstract class AbstractMapper<T extends Identificable> implements
		Mapper<T> {

	@Override
	public Key createKey(long id) {

		return KeyFactory.createKey(getType(), id);
	}

	@Override
	public String getType() {

		return getTypeClass().getSimpleName();
	}

	@SuppressWarnings("unchecked")
	protected Class<T> getTypeClass() {

		Class<T> type = null;
		ParameterizedType pType = (ParameterizedType) getClass()
				.getGenericSuperclass();
		Type[] typeArguments = pType.getActualTypeArguments();
		if (typeArguments != null && typeArguments.length > 0) {
			Type arg = typeArguments[0];
			if (arg instanceof Class) {
				type = (Class<T>) arg;
			}
		}
		return type;
	}

	protected Object mapRelation(Identificable identificable, Mapper<?> mapper) {

		return identificable == null ? null : mapper.createKey(identificable
				.getId());
	}

	protected Iterable<Attribute> attributes(Identificable entity) {

		return PropertyScanner.scan(entity.getClass());
	}

	public Entity unwrap(T identificable) {
		Entity entity;
		if (identificable.getId() > 0) {
			entity = new Entity(createKey(identificable.getId()));
		} else {
			entity = new Entity(getType());
		}
		for (Attribute attr : attributes(identificable)) {
			entity.setProperty(attr.getName(), attr.get(identificable));
		}
		return entity;
	}
	
	@Override
	public T wrap(Entity entity) {
	
		T result = PropertyScanner.newInstance(getTypeClass());
		result.setId(entity.getKey().getId());
		for (Attribute attr : attributes(result)) {
			attr.set(result, entity.getProperty(attr.getName()));

		}
		return result;
	}

}
